blob: 54a0d94228d671f0b49dc7b41cb0d9ddc9a6efff [file] [log] [blame]
Peter Qiu326b6cf2015-09-02 11:11:42 -07001//
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//
Peter Qiu376e4042014-11-13 09:40:28 -080016
17#include "apmanager/service.h"
18
19#include <signal.h>
20
Peter Qiu376e4042014-11-13 09:40:28 -080021#include <base/strings/stringprintf.h>
Peter Qiu376e4042014-11-13 09:40:28 -080022#include <chromeos/errors/error.h>
23
Peter Qiu7a420d32015-09-22 11:25:15 -070024#if !defined(__ANDROID__)
25#include <chromeos/dbus/service_constants.h>
26#else
27#include "dbus/apmanager/dbus-constants.h"
28#endif // __ANDROID__
29
Peter Qiufb39ba42014-11-21 09:09:59 -080030#include "apmanager/manager.h"
Peter Qiu376e4042014-11-13 09:40:28 -080031
32using chromeos::dbus_utils::AsyncEventSequencer;
33using chromeos::dbus_utils::ExportedObjectManager;
Vitaly Bukaa4630922014-12-11 18:46:46 -080034using org::chromium::apmanager::ManagerAdaptor;
Peter Qiu376e4042014-11-13 09:40:28 -080035using std::string;
36
37namespace apmanager {
38
39// static.
Peter Qiufb39ba42014-11-21 09:09:59 -080040const char Service::kHostapdPath[] = "/usr/sbin/hostapd";
Peter Qiu77517302015-01-08 16:22:16 -080041const char Service::kHostapdConfigPathFormat[] =
42 "/var/run/apmanager/hostapd/hostapd-%d.conf";
Peter Qiu1dbf9fd2015-01-09 13:36:55 -080043const char Service::kHostapdControlInterfacePath[] =
44 "/var/run/apmanager/hostapd/ctrl_iface";
Peter Qiu376e4042014-11-13 09:40:28 -080045const int Service::kTerminationTimeoutSeconds = 2;
46
Peter Qiue2657852015-02-03 11:33:11 -080047// static. Service state definitions.
48const char Service::kStateIdle[] = "Idle";
49const char Service::kStateStarting[] = "Starting";
50const char Service::kStateStarted[] = "Started";
51const char Service::kStateFailed[] = "Failed";
52
Peter Qiufb39ba42014-11-21 09:09:59 -080053Service::Service(Manager* manager, int service_identifier)
Peter Qiu376e4042014-11-13 09:40:28 -080054 : org::chromium::apmanager::ServiceAdaptor(this),
Peter Qiufb39ba42014-11-21 09:09:59 -080055 manager_(manager),
Peter Qiufd02b6f2015-02-27 09:55:11 -080056 identifier_(service_identifier),
Vitaly Bukaa4630922014-12-11 18:46:46 -080057 service_path_(
58 base::StringPrintf("%s/services/%d",
59 ManagerAdaptor::GetObjectPath().value().c_str(),
60 service_identifier)),
Peter Qiu376e4042014-11-13 09:40:28 -080061 dbus_path_(dbus::ObjectPath(service_path_)),
Peter Qiubf8e36c2014-12-03 22:59:45 -080062 config_(new Config(manager, service_path_)),
Peter Qiu77517302015-01-08 16:22:16 -080063 dhcp_server_factory_(DHCPServerFactory::GetInstance()),
Peter Qiu1dbf9fd2015-01-09 13:36:55 -080064 file_writer_(FileWriter::GetInstance()),
65 process_factory_(ProcessFactory::GetInstance()) {
Peter Qiu376e4042014-11-13 09:40:28 -080066 SetConfig(config_->dbus_path());
Peter Qiue2657852015-02-03 11:33:11 -080067 SetState(kStateIdle);
Peter Qiubf8e36c2014-12-03 22:59:45 -080068 // TODO(zqiu): come up with better server address management. This is good
69 // enough for now.
Peter Qiufd02b6f2015-02-27 09:55:11 -080070 config_->SetServerAddressIndex(identifier_ & 0xFF);
Peter Qiu376e4042014-11-13 09:40:28 -080071}
72
73Service::~Service() {
74 // Stop hostapd process if still running.
75 if (IsHostapdRunning()) {
Peter Qiubf8e36c2014-12-03 22:59:45 -080076 ReleaseResources();
Peter Qiu376e4042014-11-13 09:40:28 -080077 }
78}
79
80void Service::RegisterAsync(ExportedObjectManager* object_manager,
Peter Qiuc9ce1f12014-12-05 11:14:29 -080081 const scoped_refptr<dbus::Bus>& bus,
Peter Qiu376e4042014-11-13 09:40:28 -080082 AsyncEventSequencer* sequencer) {
83 CHECK(!dbus_object_) << "Already registered";
84 dbus_object_.reset(
85 new chromeos::dbus_utils::DBusObject(
86 object_manager,
Peter Qiuc9ce1f12014-12-05 11:14:29 -080087 bus,
Peter Qiu376e4042014-11-13 09:40:28 -080088 dbus_path_));
89 RegisterWithDBusObject(dbus_object_.get());
90 dbus_object_->RegisterAsync(
91 sequencer->GetHandler("Service.RegisterAsync() failed.", true));
92
93 // Register Config DBus object.
Peter Qiuc9ce1f12014-12-05 11:14:29 -080094 config_->RegisterAsync(object_manager, bus, sequencer);
Peter Qiu376e4042014-11-13 09:40:28 -080095}
96
97bool Service::Start(chromeos::ErrorPtr* error) {
98 if (IsHostapdRunning()) {
99 chromeos::Error::AddTo(
100 error, FROM_HERE, chromeos::errors::dbus::kDomain, kServiceError,
101 "Service already running");
102 return false;
103 }
104
Peter Qiubfd410e2015-01-09 15:14:20 -0800105 // Setup hostapd control interface path.
106 config_->set_control_interface(kHostapdControlInterfacePath);
107
Peter Qiu376e4042014-11-13 09:40:28 -0800108 // Generate hostapd configuration content.
109 string config_str;
110 if (!config_->GenerateConfigFile(error, &config_str)) {
111 chromeos::Error::AddTo(
112 error, FROM_HERE, chromeos::errors::dbus::kDomain, kServiceError,
113 "Failed to generate config file");
114 return false;
115 }
116
117 // Write configuration to a file.
Peter Qiu77517302015-01-08 16:22:16 -0800118 string config_file_name = base::StringPrintf(kHostapdConfigPathFormat,
Peter Qiufd02b6f2015-02-27 09:55:11 -0800119 identifier_);
Peter Qiu77517302015-01-08 16:22:16 -0800120 if (!file_writer_->Write(config_file_name, config_str)) {
Peter Qiu376e4042014-11-13 09:40:28 -0800121 chromeos::Error::AddTo(
122 error, FROM_HERE, chromeos::errors::dbus::kDomain, kServiceError,
123 "Failed to write configuration to a file");
124 return false;
125 }
126
Peter Qiufb39ba42014-11-21 09:09:59 -0800127 // Claim the device needed for this ap service.
128 if (!config_->ClaimDevice()) {
129 chromeos::Error::AddTo(
130 error, FROM_HERE, chromeos::errors::dbus::kDomain, kServiceError,
131 "Failed to claim the device for this service");
132 return false;
133 }
134
Peter Qiu376e4042014-11-13 09:40:28 -0800135 // Start hostapd process.
Peter Qiu77517302015-01-08 16:22:16 -0800136 if (!StartHostapdProcess(config_file_name)) {
Peter Qiu376e4042014-11-13 09:40:28 -0800137 chromeos::Error::AddTo(
138 error, FROM_HERE, chromeos::errors::dbus::kDomain, kServiceError,
139 "Failed to start hostapd");
Peter Qiufb39ba42014-11-21 09:09:59 -0800140 // Release the device claimed for this service.
141 config_->ReleaseDevice();
Peter Qiu376e4042014-11-13 09:40:28 -0800142 return false;
143 }
144
Peter Qiubf8e36c2014-12-03 22:59:45 -0800145 // Start DHCP server if in server mode.
146 if (config_->GetOperationMode() == kOperationModeServer) {
147 dhcp_server_.reset(
148 dhcp_server_factory_->CreateDHCPServer(config_->GetServerAddressIndex(),
149 config_->selected_interface()));
150 if (!dhcp_server_->Start()) {
151 chromeos::Error::AddTo(
152 error, FROM_HERE, chromeos::errors::dbus::kDomain, kServiceError,
153 "Failed to start DHCP server");
154 ReleaseResources();
155 return false;
156 }
Peter Qiu943cf3a2015-02-24 10:59:17 -0800157 manager_->RequestDHCPPortAccess(config_->selected_interface());
Peter Qiubf8e36c2014-12-03 22:59:45 -0800158 }
159
Peter Qiufda548c2015-01-13 14:39:19 -0800160 // Start monitoring hostapd.
161 if (!hostapd_monitor_) {
162 hostapd_monitor_.reset(
163 new HostapdMonitor(base::Bind(&Service::HostapdEventCallback,
164 base::Unretained(this)),
165 config_->control_interface(),
166 config_->selected_interface()));
167 }
168 hostapd_monitor_->Start();
169
Peter Qiue2657852015-02-03 11:33:11 -0800170 // Update service state.
171 SetState(kStateStarting);
172
Peter Qiu376e4042014-11-13 09:40:28 -0800173 return true;
174}
175
176bool Service::Stop(chromeos::ErrorPtr* error) {
177 if (!IsHostapdRunning()) {
178 chromeos::Error::AddTo(
179 error, FROM_HERE, chromeos::errors::dbus::kDomain, kServiceError,
180 "Service is not currently running");
181 return false;
182 }
183
Peter Qiubf8e36c2014-12-03 22:59:45 -0800184 ReleaseResources();
Peter Qiue2657852015-02-03 11:33:11 -0800185 SetState(kStateIdle);
Peter Qiu376e4042014-11-13 09:40:28 -0800186 return true;
187}
188
189bool Service::IsHostapdRunning() {
190 return hostapd_process_ && hostapd_process_->pid() != 0 &&
191 chromeos::Process::ProcessExists(hostapd_process_->pid());
192}
193
194bool Service::StartHostapdProcess(const string& config_file_path) {
Peter Qiu1dbf9fd2015-01-09 13:36:55 -0800195 hostapd_process_.reset(process_factory_->CreateProcess());
Peter Qiufb39ba42014-11-21 09:09:59 -0800196 hostapd_process_->AddArg(kHostapdPath);
Peter Qiu376e4042014-11-13 09:40:28 -0800197 hostapd_process_->AddArg(config_file_path);
198 if (!hostapd_process_->Start()) {
199 hostapd_process_.reset();
200 return false;
201 }
202 return true;
203}
204
205void Service::StopHostapdProcess() {
206 if (!hostapd_process_->Kill(SIGTERM, kTerminationTimeoutSeconds)) {
207 hostapd_process_->Kill(SIGKILL, kTerminationTimeoutSeconds);
208 }
209 hostapd_process_.reset();
210}
211
Peter Qiubf8e36c2014-12-03 22:59:45 -0800212void Service::ReleaseResources() {
Peter Qiue2657852015-02-03 11:33:11 -0800213 hostapd_monitor_.reset();
Peter Qiubf8e36c2014-12-03 22:59:45 -0800214 StopHostapdProcess();
215 dhcp_server_.reset();
216 config_->ReleaseDevice();
Peter Qiu943cf3a2015-02-24 10:59:17 -0800217 manager_->ReleaseDHCPPortAccess(config_->selected_interface());
Peter Qiubf8e36c2014-12-03 22:59:45 -0800218}
219
Peter Qiufda548c2015-01-13 14:39:19 -0800220void Service::HostapdEventCallback(HostapdMonitor::Event event,
221 const std::string& data) {
222 switch (event) {
Peter Qiue2657852015-02-03 11:33:11 -0800223 case HostapdMonitor::kHostapdFailed:
224 SetState(kStateFailed);
225 break;
226 case HostapdMonitor::kHostapdStarted:
227 SetState(kStateStarted);
228 break;
Peter Qiufda548c2015-01-13 14:39:19 -0800229 case HostapdMonitor::kStationConnected:
230 LOG(INFO) << "Station connected: " << data;
231 break;
232 case HostapdMonitor::kStationDisconnected:
233 LOG(INFO) << "Station disconnected: " << data;
234 break;
235 default:
236 LOG(ERROR) << "Unknown event: " << event;
237 break;
238 }
239}
240
Peter Qiu376e4042014-11-13 09:40:28 -0800241} // namespace apmanager