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/daemon_task.h"
18
19#include <base/bind.h>
20
21#if !defined(ENABLE_JSON_STORE)
22#include <glib-object.h>
23#include <glib.h>
24#endif  // ENABLE_JSON_STORE
25
26#if defined(ENABLE_BINDER)
27#include "shill/binder/binder_control.h"
28#elif defined(ENABLE_CHROMEOS_DBUS)
29#include "shill/dbus/chromeos_dbus_control.h"
30#endif  // ENABLE_BINDER, ENABLE_CHROMEOS_DBUS
31#include "shill/control_interface.h"
32#include "shill/dhcp/dhcp_provider.h"
33#include "shill/error.h"
34#include "shill/logging.h"
35#include "shill/manager.h"
36#include "shill/net/ndisc.h"
37#include "shill/net/rtnl_handler.h"
38#include "shill/process_manager.h"
39#include "shill/routing_table.h"
40#include "shill/shill_config.h"
41
42#if !defined(DISABLE_WIFI)
43#include "shill/net/netlink_manager.h"
44#include "shill/net/nl80211_message.h"
45#endif  // DISABLE_WIFI
46
47using base::Bind;
48using base::Unretained;
49using std::string;
50
51namespace shill {
52
53namespace Logging {
54static auto kModuleLogScope = ScopeLogger::kDaemon;
55static string ObjectID(DaemonTask* d) { return "(chromeos_daemon)"; }
56}
57
58DaemonTask::DaemonTask(const Settings& settings, Config* config)
59    : settings_(settings), config_(config) {}
60
61DaemonTask::~DaemonTask() {}
62
63void DaemonTask::ApplySettings() {
64  manager_->SetBlacklistedDevices(settings_.device_blacklist);
65  manager_->SetWhitelistedDevices(settings_.device_whitelist);
66  Error error;
67  manager_->SetTechnologyOrder(settings_.default_technology_order, &error);
68  CHECK(error.IsSuccess());  // Command line should have been validated.
69  manager_->SetIgnoreUnknownEthernet(settings_.ignore_unknown_ethernet);
70  if (settings_.use_portal_list) {
71    manager_->SetStartupPortalList(settings_.portal_list);
72  }
73  if (settings_.passive_mode) {
74    manager_->SetPassiveMode();
75  }
76  manager_->SetPrependDNSServers(settings_.prepend_dns_servers);
77  if (settings_.minimum_mtu) {
78    manager_->SetMinimumMTU(settings_.minimum_mtu);
79  }
80  manager_->SetAcceptHostnameFrom(settings_.accept_hostname_from);
81  manager_->SetDHCPv6EnabledDevices(settings_.dhcpv6_enabled_devices);
82}
83
84bool DaemonTask::Quit(const base::Closure& completion_callback) {
85  SLOG(this, 1) << "Starting termination actions.";
86  if (manager_->RunTerminationActionsAndNotifyMetrics(
87          Bind(&DaemonTask::TerminationActionsCompleted, Unretained(this)))) {
88    SLOG(this, 1) << "Will wait for termination actions to complete";
89    termination_completed_callback_ = completion_callback;
90    return false;  // Note to caller: don't exit yet!
91  } else {
92    SLOG(this, 1) << "No termination actions were run";
93    StopAndReturnToMain();
94    return true;  // All done, ready to exit.
95  }
96}
97
98void DaemonTask::Init() {
99  dispatcher_.reset(new EventDispatcher());
100#if defined(ENABLE_BINDER)
101  control_.reset(new BinderControl(dispatcher_.get()));
102#elif defined(ENABLE_CHROMEOS_DBUS)
103  control_.reset(new ChromeosDBusControl(dispatcher_.get()));
104#else
105// TODO(zqiu): use default stub control interface.
106#error Control interface type not specified.
107#endif  // ENABLE_BINDER, ENABLE_CHROMEOS_DBUS
108  metrics_.reset(new Metrics(dispatcher_.get()));
109  rtnl_handler_ = RTNLHandler::GetInstance();
110  routing_table_ = RoutingTable::GetInstance();
111  dhcp_provider_ = DHCPProvider::GetInstance();
112  process_manager_ = ProcessManager::GetInstance();
113#if !defined(DISABLE_WIFI)
114  netlink_manager_ = NetlinkManager::GetInstance();
115  callback80211_metrics_.reset(new Callback80211Metrics(metrics_.get()));
116#endif  // DISABLE_WIFI
117  manager_.reset(new Manager(control_.get(), dispatcher_.get(), metrics_.get(),
118                             config_->GetRunDirectory(),
119                             config_->GetStorageDirectory(),
120                             config_->GetUserStorageDirectory()));
121  control_->RegisterManagerObject(
122      manager_.get(), base::Bind(&DaemonTask::Start, base::Unretained(this)));
123  ApplySettings();
124}
125
126void DaemonTask::TerminationActionsCompleted(const Error& error) {
127  SLOG(this, 1) << "Finished termination actions.  Result: " << error;
128  metrics_->NotifyTerminationActionsCompleted(error.IsSuccess());
129
130  // Daemon::TerminationActionsCompleted() should not directly call
131  // Daemon::Stop(). Otherwise, it could lead to the call sequence below. That
132  // is not safe as the HookTable's start callback only holds a weak pointer to
133  // the Cellular object, which is destroyed in midst of the
134  // Cellular::OnTerminationCompleted() call. We schedule the
135  // Daemon::StopAndReturnToMain() call through the message loop instead.
136  //
137  // Daemon::Quit
138  //   -> Manager::RunTerminationActionsAndNotifyMetrics
139  //     -> Manager::RunTerminationActions
140  //       -> HookTable::Run
141  //         ...
142  //         -> Cellular::OnTerminationCompleted
143  //           -> Manager::TerminationActionComplete
144  //             -> HookTable::ActionComplete
145  //               -> Daemon::TerminationActionsCompleted
146  //                 -> Daemon::Stop
147  //                   -> Manager::Stop
148  //                     -> DeviceInfo::Stop
149  //                       -> Cellular::~Cellular
150  //           -> Manager::RemoveTerminationAction
151  dispatcher_->PostTask(
152      Bind(&DaemonTask::StopAndReturnToMain, Unretained(this)));
153}
154
155void DaemonTask::StopAndReturnToMain() {
156  Stop();
157  if (!termination_completed_callback_.is_null()) {
158    termination_completed_callback_.Run();
159  }
160}
161
162void DaemonTask::Start() {
163#if !defined(ENABLE_JSON_STORE)
164  g_type_init();
165#endif
166  metrics_->Start();
167  rtnl_handler_->Start(RTMGRP_LINK | RTMGRP_IPV4_IFADDR | RTMGRP_IPV4_ROUTE |
168                       RTMGRP_IPV6_IFADDR | RTMGRP_IPV6_ROUTE |
169                       RTMGRP_ND_USEROPT);
170  routing_table_->Start();
171  dhcp_provider_->Init(control_.get(), dispatcher_.get(), metrics_.get());
172  process_manager_->Init(dispatcher_.get());
173#if !defined(DISABLE_WIFI)
174  if (netlink_manager_) {
175    netlink_manager_->Init();
176    uint16_t nl80211_family_id =
177        netlink_manager_->GetFamily(Nl80211Message::kMessageTypeString,
178                                    Bind(&Nl80211Message::CreateMessage));
179    if (nl80211_family_id == NetlinkMessage::kIllegalMessageType) {
180      LOG(FATAL) << "Didn't get a legal message type for 'nl80211' messages.";
181    }
182    Nl80211Message::SetMessageType(nl80211_family_id);
183    netlink_manager_->Start();
184
185    // Install handlers for NetlinkMessages that don't have specific handlers
186    // (which are registered by message sequence number).
187    netlink_manager_->AddBroadcastHandler(
188        Bind(&Callback80211Metrics::CollectDisconnectStatistics,
189             callback80211_metrics_->AsWeakPtr()));
190  }
191#endif  // DISABLE_WIFI
192
193  manager_->Start();
194}
195
196void DaemonTask::Stop() {
197  manager_->Stop();
198  manager_ = nullptr;  // Release manager resources, including DBus adaptor.
199#if !defined(DISABLE_WIFI)
200  callback80211_metrics_ = nullptr;
201#endif  // DISABLE_WIFI
202  metrics_->Stop();
203  process_manager_->Stop();
204  dhcp_provider_->Stop();
205  metrics_ = nullptr;
206  // Must retain |control_|, as the D-Bus library may
207  // have some work left to do. See crbug.com/537771.
208}
209
210void DaemonTask::BreakTerminationLoop() {
211  // Break out of the termination loop, to continue on with other shutdown
212  // tasks.
213  brillo::MessageLoop::current()->BreakLoop();
214}
215
216}  // namespace shill
217