blob: 3bedf8a829c9ff6d7a8fdc381b17106af33d7784 [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>
22#include <chromeos/dbus/service_constants.h>
23#include <chromeos/errors/error.h>
24
Peter Qiufb39ba42014-11-21 09:09:59 -080025#include "apmanager/manager.h"
Peter Qiu376e4042014-11-13 09:40:28 -080026
27using chromeos::dbus_utils::AsyncEventSequencer;
28using chromeos::dbus_utils::ExportedObjectManager;
Vitaly Bukaa4630922014-12-11 18:46:46 -080029using org::chromium::apmanager::ManagerAdaptor;
Peter Qiu376e4042014-11-13 09:40:28 -080030using std::string;
31
32namespace apmanager {
33
34// static.
Peter Qiufb39ba42014-11-21 09:09:59 -080035const char Service::kHostapdPath[] = "/usr/sbin/hostapd";
Peter Qiu77517302015-01-08 16:22:16 -080036const char Service::kHostapdConfigPathFormat[] =
37 "/var/run/apmanager/hostapd/hostapd-%d.conf";
Peter Qiu1dbf9fd2015-01-09 13:36:55 -080038const char Service::kHostapdControlInterfacePath[] =
39 "/var/run/apmanager/hostapd/ctrl_iface";
Peter Qiu376e4042014-11-13 09:40:28 -080040const int Service::kTerminationTimeoutSeconds = 2;
41
Peter Qiue2657852015-02-03 11:33:11 -080042// static. Service state definitions.
43const char Service::kStateIdle[] = "Idle";
44const char Service::kStateStarting[] = "Starting";
45const char Service::kStateStarted[] = "Started";
46const char Service::kStateFailed[] = "Failed";
47
Peter Qiufb39ba42014-11-21 09:09:59 -080048Service::Service(Manager* manager, int service_identifier)
Peter Qiu376e4042014-11-13 09:40:28 -080049 : org::chromium::apmanager::ServiceAdaptor(this),
Peter Qiufb39ba42014-11-21 09:09:59 -080050 manager_(manager),
Peter Qiufd02b6f2015-02-27 09:55:11 -080051 identifier_(service_identifier),
Vitaly Bukaa4630922014-12-11 18:46:46 -080052 service_path_(
53 base::StringPrintf("%s/services/%d",
54 ManagerAdaptor::GetObjectPath().value().c_str(),
55 service_identifier)),
Peter Qiu376e4042014-11-13 09:40:28 -080056 dbus_path_(dbus::ObjectPath(service_path_)),
Peter Qiubf8e36c2014-12-03 22:59:45 -080057 config_(new Config(manager, service_path_)),
Peter Qiu77517302015-01-08 16:22:16 -080058 dhcp_server_factory_(DHCPServerFactory::GetInstance()),
Peter Qiu1dbf9fd2015-01-09 13:36:55 -080059 file_writer_(FileWriter::GetInstance()),
60 process_factory_(ProcessFactory::GetInstance()) {
Peter Qiu376e4042014-11-13 09:40:28 -080061 SetConfig(config_->dbus_path());
Peter Qiue2657852015-02-03 11:33:11 -080062 SetState(kStateIdle);
Peter Qiubf8e36c2014-12-03 22:59:45 -080063 // TODO(zqiu): come up with better server address management. This is good
64 // enough for now.
Peter Qiufd02b6f2015-02-27 09:55:11 -080065 config_->SetServerAddressIndex(identifier_ & 0xFF);
Peter Qiu376e4042014-11-13 09:40:28 -080066}
67
68Service::~Service() {
69 // Stop hostapd process if still running.
70 if (IsHostapdRunning()) {
Peter Qiubf8e36c2014-12-03 22:59:45 -080071 ReleaseResources();
Peter Qiu376e4042014-11-13 09:40:28 -080072 }
73}
74
75void Service::RegisterAsync(ExportedObjectManager* object_manager,
Peter Qiuc9ce1f12014-12-05 11:14:29 -080076 const scoped_refptr<dbus::Bus>& bus,
Peter Qiu376e4042014-11-13 09:40:28 -080077 AsyncEventSequencer* sequencer) {
78 CHECK(!dbus_object_) << "Already registered";
79 dbus_object_.reset(
80 new chromeos::dbus_utils::DBusObject(
81 object_manager,
Peter Qiuc9ce1f12014-12-05 11:14:29 -080082 bus,
Peter Qiu376e4042014-11-13 09:40:28 -080083 dbus_path_));
84 RegisterWithDBusObject(dbus_object_.get());
85 dbus_object_->RegisterAsync(
86 sequencer->GetHandler("Service.RegisterAsync() failed.", true));
87
88 // Register Config DBus object.
Peter Qiuc9ce1f12014-12-05 11:14:29 -080089 config_->RegisterAsync(object_manager, bus, sequencer);
Peter Qiu376e4042014-11-13 09:40:28 -080090}
91
92bool Service::Start(chromeos::ErrorPtr* error) {
93 if (IsHostapdRunning()) {
94 chromeos::Error::AddTo(
95 error, FROM_HERE, chromeos::errors::dbus::kDomain, kServiceError,
96 "Service already running");
97 return false;
98 }
99
Peter Qiubfd410e2015-01-09 15:14:20 -0800100 // Setup hostapd control interface path.
101 config_->set_control_interface(kHostapdControlInterfacePath);
102
Peter Qiu376e4042014-11-13 09:40:28 -0800103 // Generate hostapd configuration content.
104 string config_str;
105 if (!config_->GenerateConfigFile(error, &config_str)) {
106 chromeos::Error::AddTo(
107 error, FROM_HERE, chromeos::errors::dbus::kDomain, kServiceError,
108 "Failed to generate config file");
109 return false;
110 }
111
112 // Write configuration to a file.
Peter Qiu77517302015-01-08 16:22:16 -0800113 string config_file_name = base::StringPrintf(kHostapdConfigPathFormat,
Peter Qiufd02b6f2015-02-27 09:55:11 -0800114 identifier_);
Peter Qiu77517302015-01-08 16:22:16 -0800115 if (!file_writer_->Write(config_file_name, config_str)) {
Peter Qiu376e4042014-11-13 09:40:28 -0800116 chromeos::Error::AddTo(
117 error, FROM_HERE, chromeos::errors::dbus::kDomain, kServiceError,
118 "Failed to write configuration to a file");
119 return false;
120 }
121
Peter Qiufb39ba42014-11-21 09:09:59 -0800122 // Claim the device needed for this ap service.
123 if (!config_->ClaimDevice()) {
124 chromeos::Error::AddTo(
125 error, FROM_HERE, chromeos::errors::dbus::kDomain, kServiceError,
126 "Failed to claim the device for this service");
127 return false;
128 }
129
Peter Qiu376e4042014-11-13 09:40:28 -0800130 // Start hostapd process.
Peter Qiu77517302015-01-08 16:22:16 -0800131 if (!StartHostapdProcess(config_file_name)) {
Peter Qiu376e4042014-11-13 09:40:28 -0800132 chromeos::Error::AddTo(
133 error, FROM_HERE, chromeos::errors::dbus::kDomain, kServiceError,
134 "Failed to start hostapd");
Peter Qiufb39ba42014-11-21 09:09:59 -0800135 // Release the device claimed for this service.
136 config_->ReleaseDevice();
Peter Qiu376e4042014-11-13 09:40:28 -0800137 return false;
138 }
139
Peter Qiubf8e36c2014-12-03 22:59:45 -0800140 // Start DHCP server if in server mode.
141 if (config_->GetOperationMode() == kOperationModeServer) {
142 dhcp_server_.reset(
143 dhcp_server_factory_->CreateDHCPServer(config_->GetServerAddressIndex(),
144 config_->selected_interface()));
145 if (!dhcp_server_->Start()) {
146 chromeos::Error::AddTo(
147 error, FROM_HERE, chromeos::errors::dbus::kDomain, kServiceError,
148 "Failed to start DHCP server");
149 ReleaseResources();
150 return false;
151 }
Peter Qiu943cf3a2015-02-24 10:59:17 -0800152 manager_->RequestDHCPPortAccess(config_->selected_interface());
Peter Qiubf8e36c2014-12-03 22:59:45 -0800153 }
154
Peter Qiufda548c2015-01-13 14:39:19 -0800155 // Start monitoring hostapd.
156 if (!hostapd_monitor_) {
157 hostapd_monitor_.reset(
158 new HostapdMonitor(base::Bind(&Service::HostapdEventCallback,
159 base::Unretained(this)),
160 config_->control_interface(),
161 config_->selected_interface()));
162 }
163 hostapd_monitor_->Start();
164
Peter Qiue2657852015-02-03 11:33:11 -0800165 // Update service state.
166 SetState(kStateStarting);
167
Peter Qiu376e4042014-11-13 09:40:28 -0800168 return true;
169}
170
171bool Service::Stop(chromeos::ErrorPtr* error) {
172 if (!IsHostapdRunning()) {
173 chromeos::Error::AddTo(
174 error, FROM_HERE, chromeos::errors::dbus::kDomain, kServiceError,
175 "Service is not currently running");
176 return false;
177 }
178
Peter Qiubf8e36c2014-12-03 22:59:45 -0800179 ReleaseResources();
Peter Qiue2657852015-02-03 11:33:11 -0800180 SetState(kStateIdle);
Peter Qiu376e4042014-11-13 09:40:28 -0800181 return true;
182}
183
184bool Service::IsHostapdRunning() {
185 return hostapd_process_ && hostapd_process_->pid() != 0 &&
186 chromeos::Process::ProcessExists(hostapd_process_->pid());
187}
188
189bool Service::StartHostapdProcess(const string& config_file_path) {
Peter Qiu1dbf9fd2015-01-09 13:36:55 -0800190 hostapd_process_.reset(process_factory_->CreateProcess());
Peter Qiufb39ba42014-11-21 09:09:59 -0800191 hostapd_process_->AddArg(kHostapdPath);
Peter Qiu376e4042014-11-13 09:40:28 -0800192 hostapd_process_->AddArg(config_file_path);
193 if (!hostapd_process_->Start()) {
194 hostapd_process_.reset();
195 return false;
196 }
197 return true;
198}
199
200void Service::StopHostapdProcess() {
201 if (!hostapd_process_->Kill(SIGTERM, kTerminationTimeoutSeconds)) {
202 hostapd_process_->Kill(SIGKILL, kTerminationTimeoutSeconds);
203 }
204 hostapd_process_.reset();
205}
206
Peter Qiubf8e36c2014-12-03 22:59:45 -0800207void Service::ReleaseResources() {
Peter Qiue2657852015-02-03 11:33:11 -0800208 hostapd_monitor_.reset();
Peter Qiubf8e36c2014-12-03 22:59:45 -0800209 StopHostapdProcess();
210 dhcp_server_.reset();
211 config_->ReleaseDevice();
Peter Qiu943cf3a2015-02-24 10:59:17 -0800212 manager_->ReleaseDHCPPortAccess(config_->selected_interface());
Peter Qiubf8e36c2014-12-03 22:59:45 -0800213}
214
Peter Qiufda548c2015-01-13 14:39:19 -0800215void Service::HostapdEventCallback(HostapdMonitor::Event event,
216 const std::string& data) {
217 switch (event) {
Peter Qiue2657852015-02-03 11:33:11 -0800218 case HostapdMonitor::kHostapdFailed:
219 SetState(kStateFailed);
220 break;
221 case HostapdMonitor::kHostapdStarted:
222 SetState(kStateStarted);
223 break;
Peter Qiufda548c2015-01-13 14:39:19 -0800224 case HostapdMonitor::kStationConnected:
225 LOG(INFO) << "Station connected: " << data;
226 break;
227 case HostapdMonitor::kStationDisconnected:
228 LOG(INFO) << "Station disconnected: " << data;
229 break;
230 default:
231 LOG(ERROR) << "Unknown event: " << event;
232 break;
233 }
234}
235
Peter Qiu376e4042014-11-13 09:40:28 -0800236} // namespace apmanager