1//
2// Copyright (C) 2014 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 "apmanager/service.h"
18
19#include <signal.h>
20
21#include <base/bind.h>
22#include <base/strings/stringprintf.h>
23#include <brillo/errors/error.h>
24
25#if !defined(__ANDROID__)
26#include <chromeos/dbus/service_constants.h>
27#else
28#include <dbus/apmanager/dbus-constants.h>
29#endif  // __ANDROID__
30
31#if defined(__BRILLO__)
32#include "apmanager/event_dispatcher.h"
33#endif  // __BRILLO__
34
35#include "apmanager/control_interface.h"
36#include "apmanager/manager.h"
37
38using std::string;
39
40namespace apmanager {
41
42// static.
43#if !defined(__ANDROID__)
44const char Service::kHostapdPath[] = "/usr/sbin/hostapd";
45const char Service::kHostapdConfigPathFormat[] =
46    "/var/run/apmanager/hostapd/hostapd-%d.conf";
47const char Service::kHostapdControlInterfacePath[] =
48    "/var/run/apmanager/hostapd/ctrl_iface";
49#else
50const char Service::kHostapdPath[] = "/system/bin/hostapd";
51const char Service::kHostapdConfigPathFormat[] =
52    "/data/misc/apmanager/hostapd/hostapd-%d.conf";
53const char Service::kHostapdControlInterfacePath[] =
54    "/data/misc/apmanager/hostapd/ctrl_iface";
55#endif  // __ANDROID__
56
57#if defined(__BRILLO__)
58const int Service::kAPInterfaceCheckIntervalMilliseconds = 200;
59const int Service::kAPInterfaceCheckMaxAttempts = 5;
60#endif  // __BRILLO__
61
62const int Service::kTerminationTimeoutSeconds = 2;
63
64// static. Service state definitions.
65const char Service::kStateIdle[] = "Idle";
66const char Service::kStateStarting[] = "Starting";
67const char Service::kStateStarted[] = "Started";
68const char Service::kStateFailed[] = "Failed";
69
70Service::Service(Manager* manager, int service_identifier)
71    : manager_(manager),
72      identifier_(service_identifier),
73      config_(new Config(manager, service_identifier)),
74      adaptor_(manager->control_interface()->CreateServiceAdaptor(this)),
75      dhcp_server_factory_(DHCPServerFactory::GetInstance()),
76      file_writer_(FileWriter::GetInstance()),
77      process_factory_(ProcessFactory::GetInstance()) {
78  adaptor_->SetConfig(config_.get());
79  adaptor_->SetState(kStateIdle);
80  // TODO(zqiu): come up with better server address management. This is good
81  // enough for now.
82  config_->SetServerAddressIndex(identifier_ & 0xFF);
83
84#if defined(__BRILLO__)
85  event_dispatcher_ = EventDispatcher::GetInstance();
86  start_in_progress_ = false;
87#endif
88}
89
90Service::~Service() {
91  // Stop hostapd process if still running.
92  if (IsHostapdRunning()) {
93    ReleaseResources();
94  }
95}
96
97bool Service::StartInternal(Error* error) {
98  if (IsHostapdRunning()) {
99    Error::PopulateAndLog(
100        error, Error::kInternalError, "Service already running", FROM_HERE);
101    return false;
102  }
103
104  // Setup hostapd control interface path.
105  config_->set_control_interface(kHostapdControlInterfacePath);
106
107  // Generate hostapd configuration content.
108  string config_str;
109  if (!config_->GenerateConfigFile(error, &config_str)) {
110    return false;
111  }
112
113  // Write configuration to a file.
114  string config_file_name = base::StringPrintf(kHostapdConfigPathFormat,
115                                               identifier_);
116  if (!file_writer_->Write(config_file_name, config_str)) {
117    Error::PopulateAndLog(error,
118                          Error::kInternalError,
119                          "Failed to write configuration to a file",
120                          FROM_HERE);
121    return false;
122  }
123
124  // Claim the device needed for this ap service.
125  if (!config_->ClaimDevice()) {
126    Error::PopulateAndLog(error,
127                          Error::kInternalError,
128                          "Failed to claim the device for this service",
129                          FROM_HERE);
130    return false;
131  }
132
133  // Start hostapd process.
134  if (!StartHostapdProcess(config_file_name)) {
135    Error::PopulateAndLog(
136        error, Error::kInternalError, "Failed to start hostapd", FROM_HERE);
137    // Release the device claimed for this service.
138    config_->ReleaseDevice();
139    return false;
140  }
141
142  // Start DHCP server if in server mode.
143  if (config_->GetOperationMode() == kOperationModeServer) {
144    dhcp_server_.reset(
145        dhcp_server_factory_->CreateDHCPServer(config_->GetServerAddressIndex(),
146                                               config_->selected_interface()));
147    if (!dhcp_server_->Start()) {
148      Error::PopulateAndLog(error,
149                            Error::kInternalError,
150                            "Failed to start DHCP server",
151                            FROM_HERE);
152      ReleaseResources();
153      return false;
154    }
155    manager_->RequestDHCPPortAccess(config_->selected_interface());
156  }
157
158  // Start monitoring hostapd.
159  if (!hostapd_monitor_) {
160    hostapd_monitor_.reset(
161        new HostapdMonitor(base::Bind(&Service::HostapdEventCallback,
162                                      weak_factory_.GetWeakPtr()),
163                           config_->control_interface(),
164                           config_->selected_interface()));
165  }
166  hostapd_monitor_->Start();
167
168  // Update service state.
169  adaptor_->SetState(kStateStarting);
170
171  return true;
172}
173
174void Service::Start(const base::Callback<void(const Error&)>& result_callback) {
175  Error error;
176
177#if !defined(__BRILLO__)
178  StartInternal(&error);
179  result_callback.Run(error);
180#else
181  if (start_in_progress_) {
182    Error::PopulateAndLog(
183        &error, Error::kInternalError, "Start already in progress", FROM_HERE);
184    result_callback.Run(error);
185    return;
186  }
187
188  string interface_name;
189  if (!manager_->SetupApModeInterface(&interface_name)) {
190    Error::PopulateAndLog(&error,
191                          Error::kInternalError,
192                          "Failed to setup AP mode interface",
193                          FROM_HERE);
194    result_callback.Run(error);
195    return;
196  }
197
198  event_dispatcher_->PostDelayedTask(
199      base::Bind(&Service::APInterfaceCheckTask,
200                 weak_factory_.GetWeakPtr(),
201                 interface_name,
202                 0,    // Initial check count.
203                 result_callback),
204      kAPInterfaceCheckIntervalMilliseconds);
205#endif
206}
207
208bool Service::Stop(Error* error) {
209  if (!IsHostapdRunning()) {
210    Error::PopulateAndLog(error,
211                          Error::kInternalError,
212                          "Service is not currently running", FROM_HERE);
213    return false;
214  }
215
216  ReleaseResources();
217  adaptor_->SetState(kStateIdle);
218  return true;
219}
220
221#if defined(__BRILLO__)
222void Service::HandleStartFailure() {
223  // Restore station mode interface.
224  string station_mode_interface;
225  manager_->SetupStationModeInterface(&station_mode_interface);
226
227  // Reset state variables.
228  start_in_progress_ = false;
229}
230
231void Service::APInterfaceCheckTask(
232    const string& interface_name,
233    int check_count,
234    const base::Callback<void(const Error&)>& result_callback) {
235  Error error;
236
237  // Check if the AP interface is enumerated.
238  if (manager_->GetDeviceFromInterfaceName(interface_name)) {
239    // Explicitly set the interface name to avoid picking other interface.
240    config_->SetInterfaceName(interface_name);
241    if (!StartInternal(&error)) {
242      HandleStartFailure();
243    }
244    result_callback.Run(error);
245    return;
246  }
247
248  check_count++;
249  if (check_count >= kAPInterfaceCheckMaxAttempts) {
250    Error::PopulateAndLog(&error,
251                          Error::kInternalError,
252                          "Timeout waiting for AP interface to be enumerated",
253                          FROM_HERE);
254    HandleStartFailure();
255    result_callback.Run(error);
256    return;
257  }
258
259  event_dispatcher_->PostDelayedTask(
260      base::Bind(&Service::APInterfaceCheckTask,
261                 weak_factory_.GetWeakPtr(),
262                 interface_name,
263                 check_count,
264                 result_callback),
265      kAPInterfaceCheckIntervalMilliseconds);
266}
267#endif  // __BRILLO__
268
269bool Service::IsHostapdRunning() {
270  return hostapd_process_ && hostapd_process_->pid() != 0 &&
271         brillo::Process::ProcessExists(hostapd_process_->pid());
272}
273
274bool Service::StartHostapdProcess(const string& config_file_path) {
275  hostapd_process_.reset(process_factory_->CreateProcess());
276  hostapd_process_->AddArg(kHostapdPath);
277  hostapd_process_->AddArg(config_file_path);
278  if (!hostapd_process_->Start()) {
279    hostapd_process_.reset();
280    return false;
281  }
282  return true;
283}
284
285void Service::StopHostapdProcess() {
286  if (!hostapd_process_->Kill(SIGTERM, kTerminationTimeoutSeconds)) {
287    hostapd_process_->Kill(SIGKILL, kTerminationTimeoutSeconds);
288  }
289  hostapd_process_.reset();
290}
291
292void Service::ReleaseResources() {
293  hostapd_monitor_.reset();
294  StopHostapdProcess();
295  dhcp_server_.reset();
296  manager_->ReleaseDHCPPortAccess(config_->selected_interface());
297#if defined(__BRILLO__)
298  // Restore station mode interface.
299  string station_mode_interface;
300  manager_->SetupStationModeInterface(&station_mode_interface);
301#endif  // __BRILLO__
302  // Only release device after mode switching had completed, to
303  // make sure the station mode interface gets enumerated by
304  // shill.
305  config_->ReleaseDevice();
306}
307
308void Service::HostapdEventCallback(HostapdMonitor::Event event,
309                                   const std::string& data) {
310  switch (event) {
311    case HostapdMonitor::kHostapdFailed:
312      adaptor_->SetState(kStateFailed);
313      break;
314    case HostapdMonitor::kHostapdStarted:
315      adaptor_->SetState(kStateStarted);
316      break;
317    case HostapdMonitor::kStationConnected:
318      LOG(INFO) << "Station connected: " << data;
319      break;
320    case HostapdMonitor::kStationDisconnected:
321      LOG(INFO) << "Station disconnected: " << data;
322      break;
323    default:
324      LOG(ERROR) << "Unknown event: " << event;
325      break;
326  }
327}
328
329}  // namespace apmanager
330