blob: feffa6acc18eb590d791d926a48948d3f1e4a744 [file] [log] [blame]
Peter Qiu376e4042014-11-13 09:40:28 -08001// Copyright 2014 The Chromium OS Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "apmanager/service.h"
6
7#include <signal.h>
8
Peter Qiu376e4042014-11-13 09:40:28 -08009#include <base/strings/stringprintf.h>
10#include <chromeos/dbus/service_constants.h>
11#include <chromeos/errors/error.h>
12
Peter Qiufb39ba42014-11-21 09:09:59 -080013#include "apmanager/manager.h"
Peter Qiu376e4042014-11-13 09:40:28 -080014
15using chromeos::dbus_utils::AsyncEventSequencer;
16using chromeos::dbus_utils::ExportedObjectManager;
Vitaly Bukaa4630922014-12-11 18:46:46 -080017using org::chromium::apmanager::ManagerAdaptor;
Peter Qiu376e4042014-11-13 09:40:28 -080018using std::string;
19
20namespace apmanager {
21
22// static.
Peter Qiufb39ba42014-11-21 09:09:59 -080023const char Service::kHostapdPath[] = "/usr/sbin/hostapd";
Peter Qiu77517302015-01-08 16:22:16 -080024const char Service::kHostapdConfigPathFormat[] =
25 "/var/run/apmanager/hostapd/hostapd-%d.conf";
Peter Qiu1dbf9fd2015-01-09 13:36:55 -080026const char Service::kHostapdControlInterfacePath[] =
27 "/var/run/apmanager/hostapd/ctrl_iface";
Peter Qiu376e4042014-11-13 09:40:28 -080028const int Service::kTerminationTimeoutSeconds = 2;
29
Peter Qiue2657852015-02-03 11:33:11 -080030// static. Service state definitions.
31const char Service::kStateIdle[] = "Idle";
32const char Service::kStateStarting[] = "Starting";
33const char Service::kStateStarted[] = "Started";
34const char Service::kStateFailed[] = "Failed";
35
Peter Qiufb39ba42014-11-21 09:09:59 -080036Service::Service(Manager* manager, int service_identifier)
Peter Qiu376e4042014-11-13 09:40:28 -080037 : org::chromium::apmanager::ServiceAdaptor(this),
Peter Qiufb39ba42014-11-21 09:09:59 -080038 manager_(manager),
Peter Qiu376e4042014-11-13 09:40:28 -080039 service_identifier_(service_identifier),
Vitaly Bukaa4630922014-12-11 18:46:46 -080040 service_path_(
41 base::StringPrintf("%s/services/%d",
42 ManagerAdaptor::GetObjectPath().value().c_str(),
43 service_identifier)),
Peter Qiu376e4042014-11-13 09:40:28 -080044 dbus_path_(dbus::ObjectPath(service_path_)),
Peter Qiubf8e36c2014-12-03 22:59:45 -080045 config_(new Config(manager, service_path_)),
Peter Qiu77517302015-01-08 16:22:16 -080046 dhcp_server_factory_(DHCPServerFactory::GetInstance()),
Peter Qiu1dbf9fd2015-01-09 13:36:55 -080047 file_writer_(FileWriter::GetInstance()),
48 process_factory_(ProcessFactory::GetInstance()) {
Peter Qiu376e4042014-11-13 09:40:28 -080049 SetConfig(config_->dbus_path());
Peter Qiue2657852015-02-03 11:33:11 -080050 SetState(kStateIdle);
Peter Qiubf8e36c2014-12-03 22:59:45 -080051 // TODO(zqiu): come up with better server address management. This is good
52 // enough for now.
53 config_->SetServerAddressIndex(service_identifier_ & 0xFF);
Peter Qiu376e4042014-11-13 09:40:28 -080054}
55
56Service::~Service() {
57 // Stop hostapd process if still running.
58 if (IsHostapdRunning()) {
Peter Qiubf8e36c2014-12-03 22:59:45 -080059 ReleaseResources();
Peter Qiu376e4042014-11-13 09:40:28 -080060 }
61}
62
63void Service::RegisterAsync(ExportedObjectManager* object_manager,
Peter Qiuc9ce1f12014-12-05 11:14:29 -080064 const scoped_refptr<dbus::Bus>& bus,
Peter Qiu376e4042014-11-13 09:40:28 -080065 AsyncEventSequencer* sequencer) {
66 CHECK(!dbus_object_) << "Already registered";
67 dbus_object_.reset(
68 new chromeos::dbus_utils::DBusObject(
69 object_manager,
Peter Qiuc9ce1f12014-12-05 11:14:29 -080070 bus,
Peter Qiu376e4042014-11-13 09:40:28 -080071 dbus_path_));
72 RegisterWithDBusObject(dbus_object_.get());
73 dbus_object_->RegisterAsync(
74 sequencer->GetHandler("Service.RegisterAsync() failed.", true));
75
76 // Register Config DBus object.
Peter Qiuc9ce1f12014-12-05 11:14:29 -080077 config_->RegisterAsync(object_manager, bus, sequencer);
Peter Qiu376e4042014-11-13 09:40:28 -080078}
79
80bool Service::Start(chromeos::ErrorPtr* error) {
81 if (IsHostapdRunning()) {
82 chromeos::Error::AddTo(
83 error, FROM_HERE, chromeos::errors::dbus::kDomain, kServiceError,
84 "Service already running");
85 return false;
86 }
87
Peter Qiubfd410e2015-01-09 15:14:20 -080088 // Setup hostapd control interface path.
89 config_->set_control_interface(kHostapdControlInterfacePath);
90
Peter Qiu376e4042014-11-13 09:40:28 -080091 // Generate hostapd configuration content.
92 string config_str;
93 if (!config_->GenerateConfigFile(error, &config_str)) {
94 chromeos::Error::AddTo(
95 error, FROM_HERE, chromeos::errors::dbus::kDomain, kServiceError,
96 "Failed to generate config file");
97 return false;
98 }
99
100 // Write configuration to a file.
Peter Qiu77517302015-01-08 16:22:16 -0800101 string config_file_name = base::StringPrintf(kHostapdConfigPathFormat,
102 service_identifier_);
103 if (!file_writer_->Write(config_file_name, config_str)) {
Peter Qiu376e4042014-11-13 09:40:28 -0800104 chromeos::Error::AddTo(
105 error, FROM_HERE, chromeos::errors::dbus::kDomain, kServiceError,
106 "Failed to write configuration to a file");
107 return false;
108 }
109
Peter Qiufb39ba42014-11-21 09:09:59 -0800110 // Claim the device needed for this ap service.
111 if (!config_->ClaimDevice()) {
112 chromeos::Error::AddTo(
113 error, FROM_HERE, chromeos::errors::dbus::kDomain, kServiceError,
114 "Failed to claim the device for this service");
115 return false;
116 }
117
Peter Qiu376e4042014-11-13 09:40:28 -0800118 // Start hostapd process.
Peter Qiu77517302015-01-08 16:22:16 -0800119 if (!StartHostapdProcess(config_file_name)) {
Peter Qiu376e4042014-11-13 09:40:28 -0800120 chromeos::Error::AddTo(
121 error, FROM_HERE, chromeos::errors::dbus::kDomain, kServiceError,
122 "Failed to start hostapd");
Peter Qiufb39ba42014-11-21 09:09:59 -0800123 // Release the device claimed for this service.
124 config_->ReleaseDevice();
Peter Qiu376e4042014-11-13 09:40:28 -0800125 return false;
126 }
127
Peter Qiubf8e36c2014-12-03 22:59:45 -0800128 // Start DHCP server if in server mode.
129 if (config_->GetOperationMode() == kOperationModeServer) {
130 dhcp_server_.reset(
131 dhcp_server_factory_->CreateDHCPServer(config_->GetServerAddressIndex(),
132 config_->selected_interface()));
133 if (!dhcp_server_->Start()) {
134 chromeos::Error::AddTo(
135 error, FROM_HERE, chromeos::errors::dbus::kDomain, kServiceError,
136 "Failed to start DHCP server");
137 ReleaseResources();
138 return false;
139 }
Peter Qiu943cf3a2015-02-24 10:59:17 -0800140 manager_->RequestDHCPPortAccess(config_->selected_interface());
Peter Qiubf8e36c2014-12-03 22:59:45 -0800141 }
142
Peter Qiufda548c2015-01-13 14:39:19 -0800143 // Start monitoring hostapd.
144 if (!hostapd_monitor_) {
145 hostapd_monitor_.reset(
146 new HostapdMonitor(base::Bind(&Service::HostapdEventCallback,
147 base::Unretained(this)),
148 config_->control_interface(),
149 config_->selected_interface()));
150 }
151 hostapd_monitor_->Start();
152
Peter Qiue2657852015-02-03 11:33:11 -0800153 // Update service state.
154 SetState(kStateStarting);
155
Peter Qiu376e4042014-11-13 09:40:28 -0800156 return true;
157}
158
159bool Service::Stop(chromeos::ErrorPtr* error) {
160 if (!IsHostapdRunning()) {
161 chromeos::Error::AddTo(
162 error, FROM_HERE, chromeos::errors::dbus::kDomain, kServiceError,
163 "Service is not currently running");
164 return false;
165 }
166
Peter Qiubf8e36c2014-12-03 22:59:45 -0800167 ReleaseResources();
Peter Qiue2657852015-02-03 11:33:11 -0800168 SetState(kStateIdle);
Peter Qiu376e4042014-11-13 09:40:28 -0800169 return true;
170}
171
172bool Service::IsHostapdRunning() {
173 return hostapd_process_ && hostapd_process_->pid() != 0 &&
174 chromeos::Process::ProcessExists(hostapd_process_->pid());
175}
176
177bool Service::StartHostapdProcess(const string& config_file_path) {
Peter Qiu1dbf9fd2015-01-09 13:36:55 -0800178 hostapd_process_.reset(process_factory_->CreateProcess());
Peter Qiufb39ba42014-11-21 09:09:59 -0800179 hostapd_process_->AddArg(kHostapdPath);
Peter Qiu376e4042014-11-13 09:40:28 -0800180 hostapd_process_->AddArg(config_file_path);
181 if (!hostapd_process_->Start()) {
182 hostapd_process_.reset();
183 return false;
184 }
185 return true;
186}
187
188void Service::StopHostapdProcess() {
189 if (!hostapd_process_->Kill(SIGTERM, kTerminationTimeoutSeconds)) {
190 hostapd_process_->Kill(SIGKILL, kTerminationTimeoutSeconds);
191 }
192 hostapd_process_.reset();
193}
194
Peter Qiubf8e36c2014-12-03 22:59:45 -0800195void Service::ReleaseResources() {
Peter Qiue2657852015-02-03 11:33:11 -0800196 hostapd_monitor_.reset();
Peter Qiubf8e36c2014-12-03 22:59:45 -0800197 StopHostapdProcess();
198 dhcp_server_.reset();
199 config_->ReleaseDevice();
Peter Qiu943cf3a2015-02-24 10:59:17 -0800200 manager_->ReleaseDHCPPortAccess(config_->selected_interface());
Peter Qiubf8e36c2014-12-03 22:59:45 -0800201}
202
Peter Qiufda548c2015-01-13 14:39:19 -0800203void Service::HostapdEventCallback(HostapdMonitor::Event event,
204 const std::string& data) {
205 switch (event) {
Peter Qiue2657852015-02-03 11:33:11 -0800206 case HostapdMonitor::kHostapdFailed:
207 SetState(kStateFailed);
208 break;
209 case HostapdMonitor::kHostapdStarted:
210 SetState(kStateStarted);
211 break;
Peter Qiufda548c2015-01-13 14:39:19 -0800212 case HostapdMonitor::kStationConnected:
213 LOG(INFO) << "Station connected: " << data;
214 break;
215 case HostapdMonitor::kStationDisconnected:
216 LOG(INFO) << "Station disconnected: " << data;
217 break;
218 default:
219 LOG(ERROR) << "Unknown event: " << event;
220 break;
221 }
222}
223
Peter Qiu376e4042014-11-13 09:40:28 -0800224} // namespace apmanager