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