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
38 using std::string;
39
40 namespace apmanager {
41
42 // static.
43 #if !defined(__ANDROID__)
44 const char Service::kHostapdPath[] = "/usr/sbin/hostapd";
45 const char Service::kHostapdConfigPathFormat[] =
46 "/var/run/apmanager/hostapd/hostapd-%d.conf";
47 const char Service::kHostapdControlInterfacePath[] =
48 "/var/run/apmanager/hostapd/ctrl_iface";
49 #else
50 const char Service::kHostapdPath[] = "/system/bin/hostapd";
51 const char Service::kHostapdConfigPathFormat[] =
52 "/data/misc/apmanager/hostapd/hostapd-%d.conf";
53 const char Service::kHostapdControlInterfacePath[] =
54 "/data/misc/apmanager/hostapd/ctrl_iface";
55 #endif // __ANDROID__
56
57 #if defined(__BRILLO__)
58 const int Service::kAPInterfaceCheckIntervalMilliseconds = 200;
59 const int Service::kAPInterfaceCheckMaxAttempts = 5;
60 #endif // __BRILLO__
61
62 const int Service::kTerminationTimeoutSeconds = 2;
63
64 // static. Service state definitions.
65 const char Service::kStateIdle[] = "Idle";
66 const char Service::kStateStarting[] = "Starting";
67 const char Service::kStateStarted[] = "Started";
68 const char Service::kStateFailed[] = "Failed";
69
Service(Manager * manager,int service_identifier)70 Service::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
~Service()90 Service::~Service() {
91 // Stop hostapd process if still running.
92 if (IsHostapdRunning()) {
93 ReleaseResources();
94 }
95 }
96
StartInternal(Error * error)97 bool 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
Start(const base::Callback<void (const Error &)> & result_callback)174 void 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
Stop(Error * error)208 bool 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__)
HandleStartFailure()222 void 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
APInterfaceCheckTask(const string & interface_name,int check_count,const base::Callback<void (const Error &)> & result_callback)231 void 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
IsHostapdRunning()269 bool Service::IsHostapdRunning() {
270 return hostapd_process_ && hostapd_process_->pid() != 0 &&
271 brillo::Process::ProcessExists(hostapd_process_->pid());
272 }
273
StartHostapdProcess(const string & config_file_path)274 bool 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
StopHostapdProcess()285 void Service::StopHostapdProcess() {
286 if (!hostapd_process_->Kill(SIGTERM, kTerminationTimeoutSeconds)) {
287 hostapd_process_->Kill(SIGKILL, kTerminationTimeoutSeconds);
288 }
289 hostapd_process_.reset();
290 }
291
ReleaseResources()292 void 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
HostapdEventCallback(HostapdMonitor::Event event,const std::string & data)308 void 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